<html>

<head>
	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
	<title>GBA.js</title>
	<link rel="stylesheet" href="resources/main.css">
	<script type="text/javascript" src="./js/jquery-3.4.0.min.js"></script>
	<script src="js/util.js"></script>
	<script src="js/core.js"></script>
	<script src="js/arm.js"></script>
	<script src="js/thumb.js"></script>
	<script src="js/mmu.js"></script>
	<script src="js/io.js"></script>
	<script src="js/audio.js"></script>
	<script src="js/video.js"></script>
	<script src="js/video/proxy.js"></script>
	<script src="js/video/software.js"></script>
	<script src="js/irq.js"></script>
	<script src="js/keypad.js"></script>
	<script src="js/sio.js"></script>
	<script src="js/savedata.js"></script>
	<script src="js/gpio.js"></script>
	<script src="js/gba.js"></script>
	<script src="resources/xhr.js"></script>
	<canvas id="screen" width='480px' height='320px'></canvas>
	<!-- <div id="btnall"
  style="opacity:0.5;width:45vw;height:45vw;background-size:45vw 45vw;background-image:url('./img/btn.png');background-repeat:no-repeat;position:fixed;left:5vw;top:5vw;transform: rotate(90deg);">
</div> -->
	<div class="btn" id="btnup"
		style="opacity:0.5;width:15vw;height:15vw;background-size:15vw 15vw;background-image:url('./img/btn.png');background-repeat:no-repeat;position:fixed;left:35vw;top:20vw;transform: rotate(90deg);">
	</div>
	<div class="btn" id="btnright"
		style="opacity:0.5;width:15vw;height:15vw;background-size:15vw 15vw;background-image:url('./img/btn.png');background-repeat:no-repeat;position:fixed;left:20vw;top:35vw;transform: rotate(180deg);">
	</div>
	<div class="btn" id="btn12"
		style="width:15vw;height:15vw;background-size:15vw 15vw;background-image:url('./img/empty.png');background-repeat:no-repeat;position:fixed;left:35vw;top:35vw;transform: rotate(180deg);">
	</div>
	<div class="btn" id="btndown"
		style="opacity:0.5;width:15vw;height:15vw;background-size:15vw 15vw;background-image:url('./img/btn.png');background-repeat:no-repeat;position:fixed;left:5vw;top:20vw;transform: rotate(270deg);">
	</div>
	<div class="btn" id="btn23"
		style="width:15vw;height:15vw;background-size:15vw 15vw;background-image:url('./img/empty.png');background-repeat:no-repeat;position:fixed;left:5vw;top:35vw;transform: rotate(180deg);">
	</div>
	<div class="btn" id="btnleft"
		style="opacity:0.5;width:15vw;height:15vw;background-size:15vw 15vw;background-image:url('./img/btn.png');background-repeat:no-repeat;position:fixed;left:20vw;top:5vw;">
	</div>
	<div class="btn" id="btn34"
		style="width:15vw;height:15vw;background-size:15vw 15vw;background-image:url('./img/empty.png');background-repeat:no-repeat;position:fixed;left:5vw;top:5vw;transform: rotate(180deg);">
	</div>
	<div class="btn" id="btn14"
		style="width:15vw;height:15vw;background-size:15vw 15vw;background-image:url('./img/empty.png');background-repeat:no-repeat;position:fixed;left:35vw;top:5vw;transform: rotate(180deg);">
	</div>
	<div class="btn" id="btnsave"
		style="opacity:0.5;width:20vw;height:8.88vw;background-size:20vw 8.88vw;background-image:url('./img/btnsave.png');background-repeat:no-repeat;position:fixed;right:2vw;top:10vw;transform: rotate(90deg);">
	</div>
	<div class="btn" id="btnload"
		style="display:none;opacity:0.5;width:20vw;height:8.88vw;background-size:20vw 8.88vw;background-image:url('./img/btnload.png');background-repeat:no-repeat;position:fixed;right:2vw;top:10vw;transform: rotate(90deg);">
	</div>
	<div class="btn" id="btnspeed"
		style="opacity:0.5;width:20vw;height:8.88vw;background-size:20vw 8.88vw;background-image:url('./img/btnspeed.png');background-repeat:no-repeat;position:fixed;right:2vw;top:35vw;transform: rotate(90deg);">
	</div>
	<div class="btn" id="btnselect"
		style="opacity:0.5;width:20vw;height:8.88vw;background-size:20vw 8.88vw;background-image:url('./img/btnselect.png');background-repeat:no-repeat;position:fixed;right:2vw;bottom:35vw;transform: rotate(90deg);">
	</div>
	<div class="btn" id="btnstart"
		style="opacity:0.5;width:20vw;height:8.88vw;background-size:20vw 8.88vw;background-image:url('./img/btnstart.png');background-repeat:no-repeat;position:fixed;right:2vw;bottom:10vw;transform: rotate(90deg);">
	</div>
	<div class="btn" id="btnb"
		style="opacity:0.5;width:20vw;height:20vw;background-size:20vw 20vw;background-image:url('./img/btnb.png');background-repeat:no-repeat;position:fixed;left:5vw;bottom:26vw;transform: rotate(90deg);">
	</div>
	<div class="btn" id="btna"
		style="opacity:0.5;width:20vw;height:20vw;background-size:20vw 20vw;background-image:url('./img/btna.png');background-repeat:no-repeat;position:fixed;left:5vw;bottom:5vw;transform: rotate(90deg);">
	</div>
	<div class="config-btn" id="config-btn"
		style="opacity: 0.5;width: 6vh;height: 6vh;background-size: 100% 100%;background-image: url(./img/config.png);background-repeat: no-repeat;position: fixed;top: 47vh;right: -3vw;transform: rotate(90deg);">
	</div>
	<script>
		var gba;
		var runCommands = [];
		var debug = null;
		var keyCodeDic = {
			KEYCODE_LEFT: 37,
			KEYCODE_UP: 38,
			KEYCODE_RIGHT: 39,
			KEYCODE_DOWN: 40,
			KEYCODE_START: 13,
			KEYCODE_SELECT: 220,
			KEYCODE_A: 90,
			KEYCODE_B: 88,
			KEYCODE_L: 65,
			KEYCODE_R: 83
		}
		function pressLikeKeyboard(keyType, pressType) {
			console.log(keyType, keyCodeDic[keyType], pressType)
			gba.keypad.keyboardHandler({
				type: pressType,
				keyCode: keyCodeDic[keyType],
				preventDefault: () => { }
			})
		}
		function keyDown(keyType) {
			// 处理The AudioContext was not allowed to start. It must be resumed (or created) after a user gesture on the page. 导致没有声音的问题
			if (gba.audio.context.state !== 'running') {
				gba.audio.context.resume().then(() => {
					console.log('Playback resumed successfully');
				});
			}
			pressLikeKeyboard(keyType, 'keydown')
		}
		function keyUp(keyType) {
			pressLikeKeyboard(keyType, 'keyup')
		}
		var screen = $("#screen");
		// screen[0].style.height ="500px";
		screen.attr('height', window.innerWidth + "px");
		screen.attr('width', window.innerWidth / 320 * 480 + "px");
		var m = (window.innerWidth / 320 * 480 - window.innerWidth) / 2;
		var n = (window.innerHeight / 320 * 480 - window.innerHeight) / 2;
		// screen[0].style.transform="translate(-50%, -50%)";
		screen[0].style.transform = "rotate(90deg) translate(0px," + m + "px)";
		screen[0].style.left = 0;
		screen[0].style.right = 0;
		screen[0].style.top = 0;
		screen[0].style.bottom = 0;
		screen[0].style.margin = "auto auto";


		$('#btnup').bind("touchstart mousedown", function (evt) {
			evt.preventDefault();
			keyDown('KEYCODE_UP');
		});
		$('#btnup').bind("touchend mouseup", function (evt) {
			evt.preventDefault();
			keyUp('KEYCODE_UP');
		});
		$('#btnright').bind("touchstart mousedown", function (evt) {
			evt.preventDefault();
			keyDown('KEYCODE_RIGHT');
		});
		$('#btnright').bind("touchend mouseup", function (evt) {
			evt.preventDefault();
			keyUp('KEYCODE_RIGHT');
		});

		$('#btn12').bind("touchstart mousedown", function (evt) {
			evt.preventDefault();
			keyDown('KEYCODE_UP');
			keyDown('KEYCODE_RIGHT');
		});
		$('#btn12').bind("touchend mouseup", function (evt) {
			evt.preventDefault();
			keyUp('KEYCODE_UP');
			keyUp('KEYCODE_RIGHT');
		});
		$('#btn14').bind("touchstart mousedown", function (evt) {
			evt.preventDefault();
			keyDown('KEYCODE_UP');
			keyDown('KEYCODE_LEFT');
		});
		$('#btn14').bind("touchend mouseup", function (evt) {
			evt.preventDefault();
			keyUp('KEYCODE_UP');
			keyUp('KEYCODE_LEFT');
		});


		$('#btndown').bind("touchstart mousedown", function (evt) {
			evt.preventDefault();
			keyDown('KEYCODE_DOWN');
		});
		$('#btndown').bind("touchend mouseup", function (evt) {
			evt.preventDefault();
			keyUp('KEYCODE_DOWN');
		});

		$('#btn23').bind("touchstart mousedown", function (evt) {
			evt.preventDefault();
			keyDown('KEYCODE_RIGHT');
			keyDown('KEYCODE_DOWN');
		});
		$('#btn23').bind("touchend mouseup", function (evt) {
			evt.preventDefault();
			keyUp('KEYCODE_RIGHT');
			keyUp('KEYCODE_DOWN');
		});

		$('#btnleft').bind("touchstart mousedown", function (evt) {
			evt.preventDefault();
			keyDown('KEYCODE_LEFT');
		});
		$('#btnleft').bind("touchend mouseup", function (evt) {
			evt.preventDefault();
			keyUp('KEYCODE_LEFT');
		});

		$('#btn34').bind("touchstart mousedown", function (evt) {
			evt.preventDefault();
			keyDown('KEYCODE_DOWN');
			keyDown('KEYCODE_LEFT');
		});
		$('#btn34').bind("touchend mouseup", function (evt) {
			evt.preventDefault();
			keyUp('KEYCODE_DOWN');
			keyUp('KEYCODE_LEFT');
		});
		$('#btnsave').bind("touchstart mousedown", function (evt) {
			evt.preventDefault();
			gba.downloadSavedata()
		});
		$('#btnsave').bind("touchend mouseup", function (evt) {
			evt.preventDefault();
		});
		$('#btnload').bind("touchstart mousedown", function (evt) {
			evt.preventDefault();
			console.log('btnload')
		});
		$('#btnload').bind("touchend mouseup", function (evt) {
			evt.preventDefault();
		});
		$('#btnspeed').bind("touchstart mousedown", function (evt) {
			evt.preventDefault();
			console.log('btnspeed')
		});
		$('#btnspeed').bind("touchend mouseup", function (evt) {
			evt.preventDefault();
		});

		$('#btnselect').bind("touchstart mousedown", function (evt) {
			evt.preventDefault();
			keyDown('KEYCODE_SELECT');
		});
		$('#btnselect').bind("touchend mouseup", function (evt) {
			evt.preventDefault();
			keyUp('KEYCODE_SELECT');
		});
		$('#btnstart').bind("touchstart mousedown", function (evt) {
			evt.preventDefault();
			keyDown('KEYCODE_START');
		});
		$('#btnstart').bind("touchend mouseup", function (evt) {
			evt.preventDefault();
			keyUp('KEYCODE_START');
		});
		$('#btna').bind("touchstart mousedown", function (evt) {
			evt.preventDefault();
			keyDown('KEYCODE_A');
		});
		$('#btna').bind("touchend mouseup", function (evt) {
			evt.preventDefault();
			keyUp('KEYCODE_A');
		});
		$('#btnb').bind("touchstart mousedown", function (evt) {
			evt.preventDefault();
			keyDown('KEYCODE_B');
		});
		$('#btnb').bind("touchend mouseup", function (evt) {
			evt.preventDefault();
			keyUp('KEYCODE_B');
		});
		$('#config-btn').bind("touchstart mousedown", function (evt) {
			evt.preventDefault();
			nes.buttonDown(KEYS[89][0], KEYS[89][1]);
			isConfigShow = !isConfigShow;
			if (isConfigShow) {
				$('#game-list-box').show();
				$('#btnsave').hide();
				$('#btnload').show();
			} else {
				$('#game-list-box').hide();
				$('#btnsave').show();
				$('#btnload').hide();
			}
		});

		try {
			gba = new GameBoyAdvance();
			gba.keypad.eatInput = true;
			gba.setLogger(function (level, error) {
				console.log(error);
				gba.pause();
				var screen = document.getElementById('screen');
				if (screen.getAttribute('class') == 'dead') {
					console.log('We appear to have crashed multiple times without reseting.');
					return;
				}
				var crash = document.createElement('img');
				crash.setAttribute('id', 'crash');
				crash.setAttribute('src', 'resources/crash.png');
				screen.parentElement.insertBefore(crash, screen);
				screen.setAttribute('class', 'dead');
			});
		} catch (exception) {
			gba = null;
		}

		window.onload = function () {
			if (gba && FileReader) {
				var canvas = document.getElementById('screen');
				gba.setCanvas(canvas);

				gba.logLevel = gba.LOG_ERROR;

				loadRom('resources/bios.bin', function (bios) {
					gba.setBios(bios);
				});

				if (!gba.audio.context) {
					// Remove the sound box if sound isn't available
					var soundbox = document.getElementById('sound');
					soundbox.parentElement.removeChild(soundbox);
				}

				if (window.navigator.appName == 'Microsoft Internet Explorer') {
					// Remove the pixelated option if it doesn't work
					var pixelatedBox = document.getElementById('pixelated');
					pixelatedBox.parentElement.removeChild(pixelatedBox);
				}
			} else {
				var dead = document.getElementById('controls');
				dead.parentElement.removeChild(dead);
			}
		}

		function fadeOut(id, nextId, kill) {
			var e = document.getElementById(id);
			var e2 = document.getElementById(nextId);
			if (!e) {
				return;
			}
			var removeSelf = function () {
				if (kill) {
					e.parentElement.removeChild(e);
				} else {
					e.setAttribute('class', 'dead');
					e.removeEventListener('webkitTransitionEnd', removeSelf);
					e.removeEventListener('oTransitionEnd', removeSelf);
					e.removeEventListener('transitionend', removeSelf);
				}
				if (e2) {
					e2.setAttribute('class', 'hidden');
					setTimeout(function () {
						e2.removeAttribute('class');
					}, 0);
				}
			}

			e.addEventListener('webkitTransitionEnd', removeSelf, false);
			e.addEventListener('oTransitionEnd', removeSelf, false);
			e.addEventListener('transitionend', removeSelf, false);
			e.setAttribute('class', 'hidden');
		}
		function run(file) {
			var dead = document.getElementById('loader');
			dead.value = '';
			var load = document.getElementById('select');
			load.textContent = 'Loading...';
			load.removeAttribute('onclick');
			var pause = document.getElementById('pause');
			pause.textContent = "PAUSE";
			console.log(file);
			gba.loadRomFromFile(file, function (result) {
				if (result) {
					for (var i = 0; i < runCommands.length; ++i) {
						runCommands[i]();
					}
					runCommands = [];
					fadeOut('preload', 'ingame');
					fadeOut('instructions', null, true);
					gba.runStable();
				} else {
					load.textContent = 'FAILED';
					setTimeout(function () {
						load.textContent = 'SELECT';
						load.onclick = function () {
							document.getElementById('loader').click();
						}
					}, 3000);
				}
			});
		}
		const romurl = './rom/zalda_triangle_power_of_gods.gba'

		function loadBinary(path, callback, handleProgress) {
			var req = new XMLHttpRequest();
			// 返回的格式按二进制来
			req.responseType = "blob";
			req.open("GET", path);
			req.overrideMimeType("text/plain; charset=x-user-defined");
			req.onload = function () {
				if (this.status === 200) {
					console.log(this)
					callback(null, this.response);
				} else if (this.status === 0) {
					// Aborted, so ignore error
				} else {
					callback(new Error(req.statusText));
				}
			};
			req.onerror = function () {
				callback(new Error(req.statusText));
			};
			req.onprogress = handleProgress;
			req.send();
			return req;
		}
		loadBinary(romurl, (err, data) => {
			if (err) {
				// this.setState({ error: `Error loading ROM: ${err.message}` });
				console.log(err);
			} else {
				run(data);
			};
		})

		function reset() {
			gba.pause();
			gba.reset();
			var load = document.getElementById('select');
			load.textContent = 'SELECT';
			var crash = document.getElementById('crash');
			if (crash) {
				var context = gba.targetCanvas.getContext('2d');
				context.clearRect(0, 0, 480, 320);
				gba.video.drawCallback();
				crash.parentElement.removeChild(crash);
				var canvas = document.getElementById('screen');
				canvas.removeAttribute('class');
			} else {
				lcdFade(gba.context, gba.targetCanvas.getContext('2d'), gba.video.drawCallback);
			}
			load.onclick = function () {
				document.getElementById('loader').click();
			}
			fadeOut('ingame', 'preload');
		}

		function uploadSavedataPending(file) {
			runCommands.push(function () { gba.loadSavedataFromFile(file) });
		}

		function togglePause() {
			var e = document.getElementById('pause');
			if (gba.paused) {
				if (debug && debug.gbaCon) {
					debug.gbaCon.run();
				} else {
					gba.runStable();
				}
				e.textContent = "PAUSE";
			} else {
				if (debug && debug.gbaCon) {
					debug.gbaCon.pause();
				} else {
					gba.pause();
				}
				e.textContent = "UNPAUSE";
			}
		}

		function screenshot() {
			var canvas = gba.indirectCanvas;
			window.open(canvas.toDataURL('image/png'), 'screenshot');
		}

		function lcdFade(context, target, callback) {
			var i = 0;
			var drawInterval = setInterval(function () {
				i++;
				var pixelData = context.getImageData(0, 0, 240, 160);
				for (var y = 0; y < 160; ++y) {
					for (var x = 0; x < 240; ++x) {
						var xDiff = Math.abs(x - 120);
						var yDiff = Math.abs(y - 80) * 0.8;
						var xFactor = (120 - i - xDiff) / 120;
						var yFactor = (80 - i - ((y & 1) * 10) - yDiff + Math.pow(xDiff, 1 / 2)) / 80;
						pixelData.data[(x + y * 240) * 4 + 3] *= Math.pow(xFactor, 1 / 3) * Math.pow(yFactor, 1 / 2);
					}
				}
				context.putImageData(pixelData, 0, 0);
				target.clearRect(0, 0, 480, 320);
				if (i > 40) {
					clearInterval(drawInterval);
				} else {
					callback();
				}
			}, 50);
		}

		function setVolume(value) {
			gba.audio.masterVolume = Math.pow(2, value) - 1;
		}

		function setPixelated(pixelated) {
			var screen = document.getElementById('screen');
			var context = screen.getContext('2d');
			if (context.webkitImageSmoothingEnabled) {
				context.webkitImageSmoothingEnabled = !pixelated;
			} else if (context.mozImageSmoothingEnabled) {
				context.mozImageSmoothingEnabled = !pixelated;
			} else if (window.navigator.appName != 'Microsoft Internet Explorer') {
				if (pixelated) {
					screen.setAttribute('width', '240');
					screen.setAttribute('height', '160');
				} else {
					screen.setAttribute('width', '480');
					screen.setAttribute('height', '320');
				}
				if (window.navigator.appName == 'Opera') {
					// Ugly hack! Ew!
					if (pixelated) {
						screen.style.marginTop = '0';
						screen.style.marginBottom = '-325px';
					} else {
						delete screen.style;
					}
				}
			}
		}

		function enableDebug() {
			window.onmessage = function (message) {
				if (message.origin != document.domain && (message.origin != 'file://' || document.domain)) {
					console.log('Failed XSS');
					return;
				}
				switch (message.data) {
					case 'connect':
						if (message.source == debug) {
							debug.postMessage('connect', document.domain || '*');
						}
						break;
					case 'connected':
						break;
					case 'disconnect':
						if (message.source == debug) {
							debug = null;
						}
				}
			}
			window.onunload = function () {
				if (debug && debug.postMessage) {
					debug.postMessage('disconnect', document.domain || '*');
				}
			}
			if (!debug || !debug.postMessage) {
				debug = window.open('debugger.html', 'debug');
			} else {
				debug.postMessage('connect', document.domain || '*');
			}
		}

		document.addEventListener('webkitfullscreenchange', function () {
			var canvas = document.getElementById('screen');
			if (document.webkitIsFullScreen) {
				canvas.setAttribute('height', document.body.offsetHeight);
				canvas.setAttribute('width', document.body.offsetHeight / 2 * 3);
				canvas.setAttribute('style', 'margin: 0');
			} else {
				canvas.setAttribute('height', 320);
				canvas.setAttribute('width', 480);
				canvas.removeAttribute('style');
			}
		}, false);
	</script>
</head>

<body>
	<div style="display:none">
		<section id="controls">
			<div id="preload">
				<button class="bigbutton" id="select"
					onclick="document.getElementById('loader').click()">SELECT</button>
				<input id="loader" type="file" accept=".gba" onchange="run(this.files[0]);">
				<button onclick="document.getElementById('saveloader').click()">Upload Savegame</button>
				<input id="saveloader" type="file" onchange="uploadSavedataPending(this.files[0]);">
			</div>
			<div id="ingame" class="hidden">
				<button id="pause" class="bigbutton" onclick="togglePause()">PAUSE</button>
				<button class="bigbutton" onclick="reset()">RESET</button>
				<button onclick="gba.downloadSavedata()">Download Savegame</button>
				<button onclick="screenshot()">Screenshot</button>
				<label id="pixelated">
					<input type="checkbox" onchange="setPixelated(this.checked)">
					<p>Pixelated</p>
				</label>
				<div id="sound">
					<input type="checkbox" checked onchange="gba.audio.masterEnable = this.checked">
					<p>Sound</p>
					<input type="range" min="0" max="1" value="1" step="any" onchange="setVolume(this.value)"
						oninput="setVolume(this.value)">
				</div>
				<p id="openDebug" onclick="enableDebug()">Debugger</p>
			</div>
		</section>
	</div>
</body>

</html>